#include "threshold.h"

#include <QDebug>

Threshold::Threshold()
{
}

Threshold::Threshold(QImage source, int* grayInfo, int minGray, int maxGray, TMode mode)
{
    m_threshold1 = -1;

    m_minGray = minGray;
    m_maxGray = maxGray;
    m_totalPixels = source.width() * source.height();

    for (int i = 0; i < 256; i++)
    {
        m_grayInfo[i] = grayInfo[i];
        m_probability[i] = (double)grayInfo[i] / m_totalPixels;
    }

    m_mode = mode;
    m_source = QImage(source);

    computeThreshold();
    createImage();
}

void Threshold::computeThreshold()
{
    switch (m_mode)
    {
    //OTSU
    case OTSU:
    default:
    {
        double avg = 0;
        for (int i = m_minGray; i <= m_maxGray; i++)
        {
            avg += i * m_probability[i];
        }

        m_threshold2 = m_minGray;
        double maxVariance = -1;

        for (int i = m_minGray; i <= m_maxGray; i++)
        {
            double p1 = 0, p2 = 0, avg1 = 0, avg2 = 0;
            for (int j = m_minGray; j <= i; j++)
            {
                p1 += m_probability[j];
                avg1 += m_probability[j] * j;
            }
            p2 = 1 - p1;
            avg2 = avg - avg1;
            avg1 = avg1 / p1;
            avg2 = avg2 / p2;

            double variance = p1 * p2 * (avg1 - avg2) * (avg1 - avg2);
            if (maxVariance < variance)
            {
                maxVariance = variance;
                m_threshold2 = i;
            }
        }
    }
        break;
    //Entropy
    case ENTROPY:
    {
        double avg = 0;
        for (int i = m_minGray; i <= m_maxGray; i++)
        {
            avg += i * m_probability[i];
        }

        double minEntropy = 65535;
        m_threshold2 = m_minGray;

        for (int i = m_minGray; i <= m_maxGray; i++)
        {
            double p1 = 0, p2 = 0, avg1 = 0, avg2 = 0;
            for (int j = m_minGray; j <= i; j++)
            {
                p1 += m_probability[j];
                avg1 += j * m_probability[j];
            }
            p2 = 1 - p1;
            avg2 = avg - avg1;
            avg1 = avg1 / p1;
            avg2 = avg2 / p2;

            double entropy = 0;
            for (int j = m_minGray; j <= m_maxGray; j++)
            {
                if ((int)(m_probability[j] * 1e16) != 0)
                {
                    double tempEntropy = 0;
                    if (j <= i)
                    {
                        if ((int)(avg1 * 1e16) != 0)
                        {
                            tempEntropy = log(m_probability[j] / avg1);
                        }
                    }
                    else
                    {
                        if ((int)(avg2 * 1e16) != 0)
                        {
                            tempEntropy = log(m_probability[j] / avg2);
                        }
                    }
                    entropy += j * m_probability[j] * tempEntropy / log(2);
                }
            }

            if (minEntropy > entropy)
            {
                minEntropy = entropy;
                m_threshold2 = i;
            }
        }
    }
        break;
    }
    if (m_threshold2 == 0)
    {
        m_threshold2 = 1;
    }
}

void Threshold::createImage()
{
    m_image = QImage(m_source.size(), QImage::Format_Indexed8);

    int width = m_source.width();
    int height = m_source.height();

    for (int i = 0; i < height; i++)
    {
        uchar* sourceRgb = m_source.scanLine(i);
        uchar* imageRgb = m_image.scanLine(i);
        for (int j = 0; j < width; j++, sourceRgb++, imageRgb++)
        {
            int gray = *sourceRgb;
            if (gray >= m_threshold2)
            {
                *imageRgb = 255;
            }
            else if (gray >= m_threshold1)
            {
                *imageRgb = 0;
            }
            else
            {
                *imageRgb = 255;
            }
        }
    }
}

QImage Threshold::getImage()
{
    return m_image;
}

void Threshold::setThreshold1(int threshold)
{
    m_threshold1 = threshold;
    createImage();
}

void Threshold::setThreshold2(int threshold)
{
    m_threshold2 = threshold;
    createImage();
}

int Threshold::getThreshold()
{
    return m_threshold2;
}
